﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Homework2
{
    public static class Oql
    {
        // *************************************************************************************************
        // In some cases, to make the tree view look proper, these ops will return a list beginning with an
        // empty WebNode, the FauxRoot.  The ops will check for the FauxRoot being the only item in the list
        // and operate on its children instead of the root list itself if FauxRoot is there.
        // *************************************************************************************************

        private static readonly WebNode FauxRoot = new WebNode(null, null);
        private static readonly List<WebNode> FauxRootList = new List<WebNode> { FauxRoot }; 
        
        /// <summary>
        /// Checks whether this list is the 'faux root list', if it is then it returns the children
        /// of the 'faux root', otherwise it returns back the original list
        /// </summary>
        private static List<WebNode> CheckForFauxRoot(this List<WebNode> list)
        {
            return list == FauxRootList ? FauxRoot.Children : list;
        }

        /// <summary>
        /// Returns a new list of WebNodes without the first item of the given list
        /// </summary>
        /// <returns>List of WebNodes corresponding to the 'tail' or an empty list if an error occurred or the root list was empty</returns>
        public static List<WebNode> Tail(this List<WebNode> root)
        {
            try
            {
                // If the root list is empty, we will just return it back
                if (root.Count == 0)
                { return root; }

                var opOnMe = root.CheckForFauxRoot();
                return opOnMe.Skip(1).ToList();
            }
            catch (Exception ex)
            {
                Console.WriteLine("TAIL: Exception occurred: " + ex);
                return new List<WebNode>();
            }
        }

        /// <summary>
        /// Gets the first x 'simple trees' (in this case, child WebNodes of the root correspond to simple trees)
        /// </summary>
        /// <param name="x">Number of simple trees to get; defaults to 1</param>
        /// <returns>List of WebNodes corresponding to the first 'x' simple trees, or an empty list if an error occurred or the root list was empty</returns>
        public static List<WebNode> Head(this List<WebNode> root, int x = 1)
        {
            try
            {
                // If the root list is empty, we will just return it back
                if (root.Count == 0)
                { return root; }

                var opOnMe = root.CheckForFauxRoot();
                return opOnMe.Take(x).ToList();
            }
            catch (Exception ex)
            {
                Console.WriteLine("HEAD: Exception occurred: " + ex);
                return new List<WebNode>();
            }
        }

        /// <summary>
        /// Returns the first subtree
        /// <para>*** By default, this returns the subtree under a 'faux root', provide true to the method
        /// to have this directly return the subtree instead</para>
        /// </summary>
        /// <returns>List of WebNodes corresponding to the first subtree, or an empty list if an error occurred or the root list was empty</returns>
        public static List<WebNode> Prime(this List<WebNode> root, bool overrideFauxRoot = false)
        {
            try
            {
                // If the root list is empty, we will just return it back
                if (root.Count == 0)
                { return root; }

                var first = root.First(); // First simple tree

                if (!overrideFauxRoot)
                {
                    FauxRoot.Children.Clear();
                    FauxRoot.Children.AddRange(first.Children); // first.Children is the first subtree
                    return FauxRootList;
                }
                else
                {
                    return first.Children; // First subtree
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("HEAD: Exception occurred: " + ex);
                return new List<WebNode>();
            }
        }

        #region Concatenation Ops

        //TODO: Should these return a 'faux root'?

        /// <summary>
        /// Returns a list of WebNodes containing this node followed by the given node
        /// </summary>
        public static List<WebNode> Concat(this WebNode first, WebNode second)
        {
            return new List<WebNode> { first, second };
        }

        /// <summary>
        /// Returns a list of WebNodes containing this node followed by the given nodes
        /// </summary>
        public static List<WebNode> Concat(this WebNode first, List<WebNode> second)
        {
            var newList = new List<WebNode> { first };
            newList.AddRange(second);
            return newList;
        }

        /// <summary>
        /// Returns a list of WebNodes containing these nodes followed by the given node
        /// </summary>
        public static List<WebNode> Concat(this List<WebNode> first, WebNode second)
        {
            var newList = new List<WebNode>(first);
            newList.Add(second);
            return newList;
        }

        /// <summary>
        /// Returns a list of WebNodes containing these nodes followed by the given nodes
        /// </summary>
        public static List<WebNode> Concat(this List<WebNode> first, List<WebNode> second)
        {
            var newList = new List<WebNode>(first);
            newList.AddRange(second);
            return newList;
        }

        #endregion

        /// <summary>
        /// Creates a new WebNode using the given values and adds the given WebNode list as the node's children
        /// </summary>
        /// <returns>A list containing the new WebNode</returns>
        public static List<WebNode> Hang(string tag, string name, string text, string bse, string url, List<WebNode> second)
        {
            var newNode = new WebNode(null, null)
            {
                Tag = tag,
                Name = name,
                Text = text,
                Base = bse,
                Url = url
            };
            newNode.Children.AddRange(second);
            return new List<WebNode> { newNode };
        }

        public static string Peek(this WebNode node, string propName)
        {
            string propValue = null;
            switch (propName.ToLower())
            {
                case "tag":
                    propValue = node.Tag;
                    break;

                case "name":
                    propValue = node.Name;
                    break;

                case "text":
                    propValue = node.Text;
                    break;

                case "base":
                    propValue = node.Base;
                    break;

                case "url":
                    propValue = node.Url;
                    break;
            }
            return propValue;
        }

        public static List<string> Peek(this List<WebNode> root, string propName)
        {
            var peekList = new List<string>();

            var lowerPropName = propName.ToLower();
            foreach (var node in root)
            {
                string propValue;
                switch (lowerPropName)
                {
                    case "tag":
                        propValue = node.Tag;
                        break;

                    case "name":
                        propValue = node.Name;
                        break;

                    case "text":
                        propValue = node.Text;
                        break;

                    case "base":
                        propValue = node.Base;
                        break;

                    case "url":
                        propValue = node.Url;
                        break;

                    default:
                        return peekList; // propName was invalid
                }
                peekList.Add(propValue);
            }
            return peekList;
        }
    }
}
